#include "EPollPoller.h"

#include <cassert>
#include <cerrno>
#include <sys/epoll.h>

#include "../tcp/Channel.h"
#include "../../base/log/Logging.h"

const int kNew = -1;
const int kAdded = 1;
const int kDeleted = 2;

EPollPoller::EPollPoller(EventLoop *loop) :
        Poller(loop),
        epollfd_(::epoll_create1(EPOLL_CLOEXEC)),
        events_(kInitEventListSize) {
    if (epollfd_ < 0) {
        // epoll_create1回返回一个指向新的epoll实例的文件描述符, 这个文件描述符用于后续调用epoll的所有接口
        // 当出现这个问题的时候就代表初始化失败了
        LOG_SYSERR << "EPollerPoller构造函数初始化失败";
    }
}

EPollPoller::~EPollPoller() {
    ::close(epollfd_);
}

Timestamp EPollPoller::poll(int timeoutMs, EPollPoller::ChannelList *activeChannels) {
    LOG_TRACE << "EPollPoller::poll调用监听, 监听数量 : " << channels_.size();
    int numEvents = ::epoll_wait(epollfd_,
                                 &*events_.begin(),
                                 static_cast<int>(events_.size()),
                                 timeoutMs);
    int savedErrno = errno;
    Timestamp now(Timestamp::now());
    if (numEvents > 0) {
        fillActiveChannels(numEvents, activeChannels);
        // 获取活动事件
        if (static_cast<size_t>(numEvents) == events_.size()) {
            events_.resize(events_.size() * 2);
            // 如果发现返回的事件数量大于我们定义的事件集合, 就进行动态扩容
        }
    } else if (numEvents == 0) {
        // 出现错误了
        LOG_TRACE << "EPollPoller::poll 调用监听无事发生";
    } else {
        if (savedErrno != EINTR) {
            errno = savedErrno;
            LOG_SYSERR << "EPollPoller::poll()";
        }
    }
    return now;
}

void EPollPoller::fillActiveChannels(int numEvents, EPollPoller::ChannelList *activeChannels) const {
    assert(static_cast<size_t>(numEvents) <= events_.size());
    for (int i = 0; i < numEvents; ++i) {
        Channel *channel = static_cast<Channel *>(events_[i].data.ptr);
        int fd = channel->fd();
        // 获取文件描述符
        ChannelMap::const_iterator it = channels_.find(fd);
        // 寻找对应的处理函数
        assert(it != channels_.end());
        assert(it->second == channel);

        channel->set_revents(events_[i].events);
        // 设置对应发生的事件
        activeChannels->push_back(channel);
    }
}

void EPollPoller::updateChannel(Channel *channel) {
    assertInLoopThread();
    // 先保证在IO线程中执行当前操作
    const int index = channel->index();
    if (index == kNew || index == kDeleted) {
        // 当前是一个新的文件描述符, 添加到epoll的监听队列中去
        int fd = channel->fd();
        if (index == kNew) {
            assert(channels_.find(fd) == channels_.end());
            channels_[fd] = channel;
        } else {
            // 此时为kDeleted状态, 也就是这个文件描述符已经关闭过, 但被再次分配到
            assert(channels_.find(fd) != channels_.end());
            assert(channels_[fd] == channel);
        }
        channel->set_index(kAdded);
        update(EPOLL_CTL_ADD, channel);
    } else {
        int fd = channel->fd();
        assert(channels_.find(fd) != channels_.end());
        assert(channels_[fd] == channel);
        assert(index == kAdded);
        if (channel->isNoneEvent()) {
            update(EPOLL_CTL_DEL, channel);
            channel->set_index(kDeleted);
        } else {
            update(EPOLL_CTL_MOD, channel);
        }
    }
}

void EPollPoller::removeChannel(Channel *channel) {
    assertInLoopThread();
    // 先保证在IO线程中执行当前操作

    LOG_TRACE << "EPollPoller::removeChannel调用, fd [" << channel->fd()
              << "] 被移除";

    int fd = channel->fd();
    assert(channels_.find(fd) != channels_.end());
    assert(channels_[fd] == channel);
    assert(channel->isNoneEvent());
    int index = channel->index();
    assert(index == kAdded || index == kDeleted);
    size_t n = channels_.erase(fd);
    assert(n == 1);
    if (index == kAdded) {
        update(EPOLL_CTL_DEL, channel);
    }
    channel->set_index(kNew);
}


void EPollPoller::update(int operation, Channel *channel) {
    struct epoll_event event;
    bzero(&event, sizeof event);
    event.events = channel->events();
    event.data.ptr = channel;
    int fd = channel->fd();
    LOG_TRACE << "epoll_ctl op = " << operationToString(operation)
              << " fd = " << fd << " event = { " << channel->eventsToString() << " }";
    if (epoll_ctl(epollfd_, operation, fd, &event) < 0) {
        // 将文件描述符添加到epoll的监听队列中
        if (operation == EPOLL_CTL_DEL) {
            LOG_SYSERR << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;
        } else {
            LOG_SYSFATAL << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd;
        }
    }
}

const char *EPollPoller::operationToString(int op) {
    switch (op) {
        case EPOLL_CTL_ADD:
            return "ADD";
        case EPOLL_CTL_DEL:
            return "DEL";
        case EPOLL_CTL_MOD:
            return "MOD";
        default:
            assert(false && "ERROR op");
            return "Unknown Operation";
    }
}

